package com.spring.arch.common.converter;


import com.spring.arch.common.dto.AbstractAuditableDTO;
import com.spring.arch.common.dto.AbstractLogicalDTO;
import com.spring.arch.common.dto.AbstractPersistableDTO;
import com.spring.arch.common.dto.result.ListResultDTO;
import com.spring.arch.common.dto.result.PageResultDTO;
import com.spring.arch.common.dto.result.ResultDTO;
import com.spring.arch.common.jpa.model.AbstractAuditableModel;
import com.spring.arch.common.jpa.model.AbstractLogicalModel;
import com.spring.arch.common.jpa.model.AbstractPersistableModel;
import com.spring.arch.common.jpa.model.AbstractSaaSModel;
import com.spring.arch.common.security.UserPrincipal;
import com.spring.arch.common.dto.AbstractAuditableDTO;
import com.spring.arch.common.dto.AbstractLogicalDTO;
import com.spring.arch.common.dto.AbstractPersistableDTO;
import com.spring.arch.common.dto.result.ListResultDTO;
import com.spring.arch.common.dto.result.PageResultDTO;
import com.spring.arch.common.dto.result.ResultDTO;
import com.spring.arch.common.exception.AppRuntimeException;
import com.spring.arch.common.exception.ExceptionHolder;
import com.spring.arch.common.jpa.model.AbstractAuditableModel;
import com.spring.arch.common.jpa.model.AbstractLogicalModel;
import com.spring.arch.common.jpa.model.AbstractPersistableModel;
import com.spring.arch.common.jpa.model.AbstractSaaSModel;
import com.spring.arch.common.security.CurrentUserContextHolder;
import com.spring.arch.common.security.UserPrincipal;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;

import java.lang.reflect.Field;
import java.util.List;
import java.util.stream.Collectors;

import static com.spring.arch.common.exception.GlobalErrorCode.E2000000001;

/**
 * Model与DTO互相转换基础抽象类
 */
public abstract class AbstractConverter<Model, DTO> {

    /**
     * DTO转换到Model
     */
    public abstract Model toModel(final DTO dto);

    /**
     * DTO转换到Model
     */
    public Model toSaaSModel(final DTO dto) {
        Model model = toModel(dto);
        if (model instanceof AbstractSaaSModel) {
            // 获取登陆用户
            UserPrincipal currentUser = CurrentUserContextHolder.getCurrentUser();
            if (currentUser == null) {
                throw ExceptionHolder.serviceException(E2000000001);
            }
            AbstractSaaSModel saasModel = (AbstractSaaSModel) model;
            saasModel.setProjectId(currentUser.getProjectId());
            saasModel.setTenantId(currentUser.getTenantId());
        }
        return model;
    }

    /**
     * Model转换到DTO（列表使用）
     */
    public abstract DTO toDTO(final Model model);

    /**
     * Model转换到DTO（详细和编辑视图使用）
     */
    public DTO toDTOForView(final Model model) {
        return this.toDTO(model);
    }

    /**
     * DTO列表转换到Model列表
     */
    public final List<Model> toListModel(final List<DTO> dtoList) {
        return dtoList.stream().map(this::toModel).collect(Collectors.toList());
    }

    /**
     * Model列表转换到DTO列表
     */
    public final List<DTO> toListDTO(final List<Model> modelList) {
        return modelList.stream().map(this::toDTO).collect(Collectors.toList());
    }

    /**
     * Model分页数据转换到DTO分页数据
     */
    public final Page<DTO> toPageDTO(final Page<Model> modelPage) {
        final List<Model> modelList = modelPage.getContent();
        final List<DTO> dtoList = this.toListDTO(modelList);
        final long totalElements = modelPage.getTotalElements();
        return new PageImpl<>(dtoList, this.getPageable(modelPage), totalElements);
    }

    /**
     * Model数据转换到DTO结果封装数据（列表使用）
     */
    public final ResultDTO<DTO> toResultDTO(final Model model) {
        final DTO dto = (model == null) ? null : this.toDTO(model);
        return ResultDTO.success(dto);
    }

    /**
     * Model数据转换到DTO结果封装数据（详细和编辑视图使用）
     */
    public final ResultDTO<DTO> toResultDTOForView(final Model model) {
        final DTO dto = (model == null) ? null : this.toDTOForView(model);
        return ResultDTO.success(dto);
    }

    /**
     * Model列表数据转换到DTO结果封装数据
     */
    public final ListResultDTO<DTO> toResultDTO(final List<Model> modelList) {
        final List<DTO> dtoList = this.toListDTO(modelList);
        return ListResultDTO.success(dtoList);
    }

    /**
     * Model分页数据转换到DTO结果封装数据
     */
    public final PageResultDTO<DTO> toResultDTO(final Page<Model> modelPage) {
        final List<DTO> dtoList = this.toListDTO(modelPage.getContent());
        return PageResultDTO.success(dtoList, modelPage);
    }

    /**
     * 转换审计数据到DTO
     */
    protected final void convertCommonToDTO(final AbstractPersistableModel model, final AbstractPersistableDTO dto) {
        dto.setId(model.getId());

        if (model instanceof AbstractAuditableModel) {
            final AbstractAuditableModel auditableModel = (AbstractAuditableModel)model;
            final AbstractAuditableDTO auditableDTO = (AbstractAuditableDTO)dto;
            auditableDTO.setCreatedBy(auditableModel.getCreatedBy().orElse(null));
            auditableDTO.setLastModifiedBy(auditableModel.getLastModifiedBy().orElse(null));
            auditableDTO.setCreatedDate(auditableModel.getCreatedDate().orElse(null));
            auditableDTO.setLastModifiedDate(auditableModel.getLastModifiedDate().orElse(null));
        }

        if (model instanceof AbstractLogicalModel) {
            final AbstractLogicalModel deletableModel = (AbstractLogicalModel)model;
            final AbstractLogicalDTO deletableDTO = (AbstractLogicalDTO)dto;
            deletableDTO.setDisable(deletableModel.isDisable());
        }
    }

    /**
     * 从分页结果数据中提取分页信息
     */
    protected Pageable getPageable(final Page<Model> modelPage) {
        try {
            final Field pageableField = PageImpl.class.getSuperclass().getDeclaredField("pageable");
            pageableField.setAccessible(true);
            return (Pageable)pageableField.get(modelPage);
        } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
            throw new AppRuntimeException("系统版本变化异常。");
        }
    }
}
